package com.tianji.remark.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constants.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author 小海
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    private final RabbitMqHelper rabbitMqHelper;
    private final StringRedisTemplate redisTemplate;

    /**
     * 点赞或取消点赞
     *
     * @param dto
     */
    @Override
    public void addLikeRecord(LikeRecordFormDTO dto) {
        // 1. 判断点赞的类型，点赞或取消点赞
        boolean success = dto.getLiked() ? like(dto) : unlike(dto);
        // 2. 判断是否操作成功
        if (success) {
            // 3. 成功，统计当前业务的点赞数量，存到Redis里面的Zset里面(key是业务类型,member是业务id,score是点赞数量)
            String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + dto.getBizType() + ":" + dto.getBizId();
            Long likeTimes = redisTemplate.opsForSet().size(key);
            if (likeTimes == null) {
                return;
            }
            Boolean result = redisTemplate.opsForZSet().add(
                    RedisConstants.LIKES_TIMES_KEY_PREFIX + dto.getBizType(),
                    dto.getBizId().toString(),
                    likeTimes
            );
            /*// 4. 发送mq
            rabbitMqHelper.send(
                    MqConstants.Exchange.LIKE_RECORD_EXCHANGE,
                    StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, dto.getBizType()),
                    new LikedTimesDTO(dto.getBizId(),likeTimes));*/
        }

    }

    /**
     * 根据用户id和业务id查询点赞状态
     *
     * @param bizType
     * @param bizIds
     * @return
     */
    @Override
    public Set<Long> queryListByUserIdAndIds(String bizType, List<Long> bizIds) {
        Set<Long> set = new HashSet<>();
        // 1.根据用户id和业务id、业务类型查询
        Long userId = UserContext.getUser();
        // 1      10    9     4
        // true false true  false
        // 返回值objects:就是管道里面多条命令(多个操作)最终结果的集合
        List<Object> objects = redisTemplate.executePipelined(new RedisCallback<Object>() {
            // 把操作redis放在doInRedis里面，就在这个管道里面执行，批量操作
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                StringRedisConnection connection = (StringRedisConnection) redisConnection;
                for (Long bizId : bizIds) {
                    // 拼接key 业务类型:业务id
                    String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + bizType + ":" + bizId;
                    connection.sIsMember(key, userId.toString());
                }
                return null;
            }
        });
        // 2.返回点赞了的业务id
        for (int i = 0; i < objects.size(); i++) {
            Boolean result = (Boolean) objects.get(i);
            if (result) {
                set.add(bizIds.get(i));
            }
        }
        return null;
    }

    /**
     * 批量读取redis点赞次数的数据，发给MQ
     *
     * @param bizType
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        // 1.从Redis取出数据
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + bizType;
        Set<ZSetOperations.TypedTuple<String>> tupleSet = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        if (CollUtils.isEmpty(tupleSet)) {
            return;
        }
        // 2.封装成LikedTimesDTO集合
        List<LikedTimesDTO> dtoList = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> tuple : tupleSet) {
            String bizId = tuple.getValue();
            Double likeTimes = tuple.getScore();
            if (bizId == null || likeTimes == null) {
                continue;
            }
            // 封装成LikedTimesDTO
            LikedTimesDTO dto = new LikedTimesDTO(Long.valueOf(bizId), likeTimes.intValue());
            dtoList.add(dto);
        }
        // 3.发送消息
        rabbitMqHelper.send(
                MqConstants.Exchange.LIKE_RECORD_EXCHANGE,
                StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
                dtoList);

    }

    // 取消点赞
    private boolean unlike(LikeRecordFormDTO dto) {
        // 直接删除点赞记录
        Long userId = UserContext.getUser();
        // 1.拼接key 业务类型:业务id
        String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + dto.getBizType() + ":" + dto.getBizId();
        // 2.从Zset里面移除元素
        Long result = redisTemplate.opsForSet().remove(key, userId.toString());
        if (result != null) {
            return result > 0;
        }
        return false;
    }

    // 点赞
    private boolean like(LikeRecordFormDTO dto) {
        // 1.拼接key 业务类型:业务id
        String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + dto.getBizType() + ":" + dto.getBizId();
        // 2.向Zset里面添加元素,并设置过期时间
        Long userId = UserContext.getUser();
        Long result = redisTemplate.opsForSet().add(key, userId.toString());
        if (result != null) {
            return result > 0;
        }
        return false;
    }
}
